﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="UiCommand.cs" company="" />
// <summary>
//   Defines a command that controls the appearance and behavior of a UI command representation (e.g. menu, tool-button).
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace FormsUtilities
{
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Linq;
    using System.Windows.Forms;

    /// <summary>
    /// The UI commands controlled by the application flow.
    /// </summary>
    public enum UiCommandNames
    {
        /// <summary>
        /// New XML comparison tab creator command.
        /// </summary>
        NewComparison,

        /// <summary>
        /// Commands common to a tab.
        /// </summary>
        TabCommon,

        /// <summary>
        /// Commands common to an XML displaying tab.
        /// </summary>
        TabXml,

        /// <summary>
        /// Commands common to a tab of XML comparison.
        /// </summary>
        TabXmlComparison,

        /// <summary>
        /// The command to close all tabs but the current one.
        /// </summary>
        TabCloseAllButThis,

        /// <summary>
        /// The command managing the referring to XML object name casing.
        /// </summary>
        IgnoreNameCase,

        /// <summary>
        /// The command managing the referring to XML object value casing.
        /// </summary>
        IgnoreValueCase,

        /// <summary>
        /// The command to ignore white-spaces in the XML objects' values.
        /// </summary>
        IgnoreWhiteSpaces,

        /// <summary>
        /// The command to view the contents of the XML trees as XML syntax.
        /// </summary>
        ViewXmlSyntax,
    }

    /// <summary>
    /// Defines a command that controls the appearance and behavior of a UI command representation (e.g. menu, tool-button).
    /// </summary>
    public class UiCommand : IUiCommand
    {
        /// <summary>
        /// Maps a command/category name with a <see cref="UiCommand"/> object maintaining relevant UI command items.
        /// </summary>
        private static Dictionary<UiCommandNames, IUiCommand> s_uiCommands = new Dictionary<UiCommandNames, IUiCommand>();

        /// <summary>
        /// The list of UI items related to this command
        /// </summary>
        private List<ToolStripItem> m_items = new List<ToolStripItem>();

        private List<IUiCommand> m_subCommands = new List<IUiCommand>();

        private bool m_isChecked = false;

        #region IUiCommand implementation

        public IEnumerable<ToolStripItem> Items
        {
            get
            {
                // First, return the parent-items:
                foreach (var item in m_items)
                {
                    yield return item;
                }

                // Traverse sub-commands and return their items as well:
                foreach (var uiCommand in m_subCommands)
                {
                    foreach (var item in uiCommand.Items)
                    {
                        yield return item;
                    }
                }
            }
        }

        public IEnumerable<IUiCommand> SubCommands
        {
            get
            {
                return m_subCommands;
            }
        }

        public bool Enabled
        {
            get
            {
                if (m_items.Any())
                {
                    return m_items[0].Enabled;
                }

                return false;
            }

            set
            {
                foreach (var item in Items)
                {
                    item.Enabled = value;
                }
            }
        }

        public bool Visible
        {
            get
            {
                if (m_items.Any())
                {
                    return m_items[0].Visible;
                }

                return false;
            }

            set
            {
                foreach (var item in Items)
                {
                    item.Visible = value;
                }
            }
        }

        public bool Checked
        {
            get
            {
                return m_isChecked;
            }

            set
            {
                m_isChecked = value;

                foreach (var item in Items)
                {
                    FormsUtilitiesSet.ActOnToolStripItem(
                        item,
                        menuItem => menuItem.Checked = value,
                        buttonItem => buttonItem.Checked = value);
                }
            }
        }

        public void RegisterUiItem(ToolStripItem newItem)
        {
            m_items.Add(newItem);
        }

        public void RegisterSubUiCommand(IUiCommand subUiCommand)
        {
            m_subCommands.Add(subUiCommand);
        }

        #endregion

        #region UiCommand utilities
        /// <summary>
        /// Add another UI tool-item to a UI command manager.
        /// </summary>
        /// <param name="commandName">The name of the UI command.</param>
        /// <param name="uiItem">The tool-item to add.</param>
        public static void RegisterUiToolItem(UiCommandNames commandName, ToolStripItem uiItem)
        {
            if (!s_uiCommands.ContainsKey(commandName))
            {
                s_uiCommands.Add(commandName, new UiCommand());
            }

            s_uiCommands[commandName].RegisterUiItem(uiItem);
        }

        /// <summary>
        /// Register a given UI command under another UI command.
        /// This means that all items on the sub-command are affected by changes made to items in the parent command.
        /// </summary>
        /// <param name="parentCommandName">The name of the parent command.</param>
        /// <param name="subCommandName">The name of the sub command to attach.</param>
        public static void RegisterUiSubCommand(UiCommandNames parentCommandName, UiCommandNames subCommandName)
        {
            if (!s_uiCommands.ContainsKey(parentCommandName))
            {
                s_uiCommands.Add(parentCommandName, new UiCommand());
            }

            if (!s_uiCommands.ContainsKey(subCommandName))
            {
                s_uiCommands.Add(subCommandName, new UiCommand());
            }

            s_uiCommands[parentCommandName].RegisterSubUiCommand(s_uiCommands[subCommandName]);
        }

        /// <summary>
        /// Run a given operation on all registered items of a UI command.
        /// </summary>
        /// <param name="commandName">Identifies the UI command.</param>
        /// <param name="activity">The activity delegate to act upon.</param>
        public static void ActOnUiCommand(UiCommandNames commandName, Action<ToolStripItem> activity)
        {
            if (!s_uiCommands.ContainsKey(commandName))
            {
                throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "There's no registered UI command by the name '{0}'", commandName));
            }

            foreach (var item in s_uiCommands[commandName].Items)
            {
                activity(item);
            }
        }

        /// <summary>
        /// Retrieves a requested <see cref="UiCommand"/> object.
        /// </summary>
        /// <param name="commandName">The key to identify the required UI command.</param>
        /// <returns>The requested UI command.</returns>
        public static IUiCommand GetUiCommand(UiCommandNames commandName)
        {
            if (!s_uiCommands.ContainsKey(commandName))
            {
                LogUi.LogFatal("Requested UI Command '{0}' was not registered yet.", commandName);

                return null;
            }

            return s_uiCommands[commandName];
        }

        /// <summary>
        /// Sets the items in the given command to be check-able (if indeed they can).
        /// Invoke this method after setting all items to the UI command.
        /// </summary>
        /// <param name="commandName">The name of the UI command to set its items checkable.</param>
        /// <param name="enableCheckOnClick">Whether to enable the check-on-click propery of the items.</param>
        public static void SetUiCommandCheckable(UiCommandNames commandName, bool enableCheckOnClick = true)
        {
            if (!s_uiCommands.ContainsKey(commandName))
            {
                throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "UI command '{0}' is not registered.", commandName));
            }

            var uiCommand = s_uiCommands[commandName];

            // Mark the items in the command as check-able
            foreach (var item in uiCommand.Items)
            {
                FormsUtilitiesSet.ActOnToolStripItem(
                    item,
                    menuItem =>
                    {
                        menuItem.CheckOnClick = enableCheckOnClick;
                        menuItem.Click += (sender, e) => uiCommand.Checked = !uiCommand.Checked;
                    },
                    buttonItem =>
                    {
                        buttonItem.CheckOnClick = enableCheckOnClick;
                        buttonItem.Click += (sender, e) => uiCommand.Checked = !uiCommand.Checked;
                    });
            }
        }

        #endregion
    }
}
